//
// SharedPtr.h
//


#ifndef _SharedPtr_H
#define _SharedPtr_H


#include "AtomicCounter.h"
#include <algorithm>

template <class T>
class SharedPtr
{
public:
	SharedPtr(): _ptr(0), _pcnt(new AtomicCounter(1))
	{
	}

	SharedPtr(T* ptr): _ptr(ptr), _pcnt(new AtomicCounter(1))
	{
	}

	SharedPtr(const SharedPtr& rhs): _ptr(rhs._ptr), _pcnt(rhs._pcnt)
	{
		(*_pcnt)++;
	}

	~SharedPtr()
	{
		try
		{
			release();
		}
		catch (...)
		{
			// ...
		}
	}

	SharedPtr& assign(T* ptr)
	{
		if (get() != ptr)
		{
			release();
			_ptr = ptr;
			_pcnt = new AtomicCounter(1);
		}
		return *this;
	}
	
	SharedPtr& assign(const SharedPtr& rhs)
	{
		if (&rhs != this)
		{
			SharedPtr tmp(rhs);
			swap(tmp);
		}
		return *this;
	}

	SharedPtr& operator = (T* ptr) 
	{
		return assign(ptr); 
	}

	SharedPtr& operator = (const SharedPtr& rhs)
	{ 
		return assign(rhs); 
	}

	void swap(SharedPtr& ptr)
	{
		std::swap(_ptr, ptr._ptr);
		std::swap(_pcnt, ptr._pcnt);
	}

	T* operator -> () { return _ptr; }

	const T* operator -> () const { return _ptr; }

	T& operator * () { return *_ptr; }

	const T& operator * () const { return *_ptr; }

	T* get() { return _ptr; }

	const T* get() const { return _ptr; }

	operator T* () { return _ptr; }
	
	operator const T* () const { return _ptr; }

	bool operator ! () const { return _ptr == 0; }

	bool isNull() const { return _ptr == 0; }

	bool operator == (const SharedPtr& ptr) const { return get() == ptr.get(); }

	bool operator == (const T* ptr) const { return get() == ptr; }

	bool operator == (T* ptr) const { return get() == ptr; }

	bool operator != (const SharedPtr& ptr) const { return get() != ptr.get(); }

	bool operator != (const T* ptr) const { return get() != ptr; }

	bool operator != (T* ptr) const { return get() != ptr; }
	
	int referenceCount() const
	{ 
		return *_pcnt; 
	}

private:

	void release()
	{
		(*_pcnt) --;
		if (0 == *_pcnt)
		{
			delete _ptr;
			_ptr = 0;
			delete _pcnt;
			_pcnt = 0;
		}
	}

private:
	T*  _ptr;

	AtomicCounter* _pcnt;
};

#endif // _SharedPtr_H
